package com.wanmait.translate.configuration.interceptor;

import com.wanmait.translate.configuration.other.SqlDeep;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Properties;


/*
 * @Author huweibin
 * @Description Mybatis的拦截器
 * @Date 11:38 2020/11/23
 **/
@Intercepts(
        {@Signature
                (type = Executor.class,method = "query",
                        args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class, CacheKey.class,BoundSql.class}
                      ),
        @Signature
                (type = StatementHandler.class, method = "query",
                        args = {Statement.class, ResultHandler.class}
                )
        }
)
@Component
public class MybatisInterceptor implements Interceptor {
    //需要的类 返回的目标类+sqldeep中的mapper类
    private ThreadLocal needNames =new ThreadLocal<String[]>();

    //方法拦截
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //String name = invocation.getMethod().getName();
        //System.out.println("进入方法{"+name+"}。。。。。。。。。。。");
        Object target= invocation.getTarget();
        if(target instanceof Executor){
            CacheKey ck = (CacheKey)invocation.getArgs()[4];
            String key = ck.toString();
            //获得mapper全限定类名加方法名
            String all = (key.split(":"))[2];
            String className=all.substring(0,all.lastIndexOf("."));
            String methodName=all.substring(all.lastIndexOf(".")+1);
            Class<?> mapperClass = Class.forName(className);
            Method[] methods = mapperClass.getMethods();
            //允许调用的mapper名
            ArrayList<String> names = null;
            for (Method method : methods) {
                if (method.getName().equals(methodName)){
                    SqlDeep sqlDeep = method.getDeclaredAnnotation(SqlDeep.class);
                    if (sqlDeep==null){
                        needNames.remove();
                        return  invocation.proceed();
                    }
                    names=new ArrayList<>();
                    //放入目标类mapper
                    names.add(className);
                    Class[] value = sqlDeep.value();
                    //放入注解类
                    if (value!=null){
                        for (int i=0;i<value.length;i++){
                            names.add(value[i].getName());
                        }
                    }
                    needNames.set(names);
                    break;
                }
            }
        }else if(target instanceof StatementHandler){
            //获得需要的mapper的全限定类型
            ArrayList<String> needList = (ArrayList<String>)needNames.get();
            if(needList==null){
                return  invocation.proceed();
            }
            StatementHandler statementHandler=(StatementHandler)target;
           // SystemMetaObject为mybayis的工具类用于反射获得属性
            MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
            //获得当前将要执行sql的全限定类名
            String  mapperName=(String) metaObject.getValue("delegate.mappedStatement.id");
            mapperName=mapperName.substring(0,mapperName.lastIndexOf("."));
            //若果不在范围内返回空集合，即属性为null
            if (needList.indexOf(mapperName)<0){
               return new ArrayList<>();
            }
        }
        return  invocation.proceed();
    }


    //获取到拦截的对象，底层也是通过代理实现的，实际上是拿到一个目标代理对象
    @Override
    public Object plugin(Object target) {
        //System.out.println("plugin.....................................................");
        return Plugin.wrap(target, this);
    }

    //获取设置的阈值等参数
    @Override
    public void setProperties(Properties properties) {
    }
}
